package oceanlife.pathfinding;

/*
 * Copyright (c) 2007 by L. Paul Chew.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

import java.awt.Polygon;
import java.awt.geom.GeneralPath;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * A Triangle is an immutable Set of exactly three Pnts.
 *
 * All Set operations are available. Individual vertices can be accessed via
 * iterator() and also via triangle.get(index).
 *
 * Note that, even if two triangles have the same vertex set, they are
 * *different* triangles. Methods equals() and hashCode() are consistent with
 * this rule.
 *
 * @author Paul Chew
 *
 * Created December 2007. Replaced general simplices with geometric triangle.
 *
 */
public class Triangle extends ArraySet<Pnt> {

    private int idNumber;                   // The id number
    private Pnt circumcenter = null;        // The triangle's circumcenter
    private boolean obstacle = false;		// is triangle is obstacle

    private static int idGenerator = 0;     // Used to create id numbers
    public static boolean moreInfo = false; // True iff more info in toString

    /**
     * @param vertices the vertices of the Triangle.
     * @throws IllegalArgumentException if there are not three distinct vertices
     */
    public Triangle (Pnt... vertices) {
        this(Arrays.asList(vertices));
    }

    /**
     * @param collection a Collection holding the Simplex vertices
     * @throws IllegalArgumentException if there are not three distinct vertices
     */
    public Triangle (Collection<? extends Pnt> collection) {
        super(collection);
        idNumber = idGenerator++;
        if (this.size() != 3)
            throw new IllegalArgumentException("Triangle must have 3 vertices");
    }

    @Override
    public String toString () {
        if (!moreInfo) return "Triangle" + idNumber;
        return "Triangle" + idNumber + super.toString();
    }
    
    public ArrayList<Line2> getEdges(){
    	ArrayList<Line2> edges = new ArrayList<Line2>();
    	Pnt[] verts = this.toArray(new Pnt[0]);
    	edges.add(new Line2(verts[0],verts[1]));
    	edges.add(new Line2(verts[1],verts[2]));
    	edges.add(new Line2(verts[2],verts[0]));
    	return edges;
    }
    
    public Polygon toPolygon(){
    	Pnt[] polygonPoints = this.toArray(new Pnt[0]);
    	int polygonSize = polygonPoints.length;
    	int[] xpoints = new int[polygonSize];
    	int[] ypoints = new int[polygonSize];
    	for(int i = 0; i < polygonSize;i++){
    		xpoints[i] = (int) Math.round(polygonPoints[i].coord(0));
    		ypoints[i] = (int) Math.round(polygonPoints[i].coord(1));
    	}
    	return new Polygon(xpoints, ypoints, polygonSize);
    }

    public GeneralPath toGeneralPath(){
    	Pnt[] generalPathPoints = this.toArray(new Pnt[0]);
    	int generalPathSize = generalPathPoints.length;
    	//double[] xpoints = new int[generalPathSize];
    	//double[] ypoints = new int[generalPathSize];
    	GeneralPath gp = new GeneralPath();
    	gp.moveTo(generalPathPoints[0].coord(0), generalPathPoints[0].coord(1));
    	for(int i = 1; i < generalPathSize;i++){
    		gp.lineTo(generalPathPoints[i].coord(0), generalPathPoints[i].coord(1));
    	}
    	gp.closePath();
    	return gp;
    }

    /**
     * Get arbitrary vertex of this triangle, but not any of the bad vertices.
     * @param badVertices one or more bad vertices
     * @return a vertex of this triangle, but not one of the bad vertices
     * @throws NoSuchElementException if no vertex found
     */
    public Pnt getVertexButNot (Pnt... badVertices) {
        Collection<Pnt> bad = Arrays.asList(badVertices);
        for (Pnt v: this) if (!bad.contains(v)) return v;
        throw new NoSuchElementException("No vertex found");
    }

    /**
     * True iff triangles are neighbors. Two triangles are neighbors if they
     * share a facet.
     * @param triangle the other Triangle
     * @return true iff this Triangle is a neighbor of triangle
     */
    public boolean isNeighbor (Triangle triangle) {
        int count = 0;
        for (Pnt vertex: this)
            if (!triangle.contains(vertex)) count++;
        return count == 1;
    }

    /**
     * Report the facet opposite vertex.
     * @param vertex a vertex of this Triangle
     * @return the facet opposite vertex
     * @throws IllegalArgumentException if the vertex is not in triangle
     */
    public ArraySet<Pnt> facetOpposite (Pnt vertex) {
        ArraySet<Pnt> facet = new ArraySet<Pnt>(this);
        if (!facet.remove(vertex))
            throw new IllegalArgumentException("Vertex not in triangle");
        return facet;
    }
    
    public Line2 getOppositeEdge(Pnt currentPnt){
    	ArrayList<Pnt> pnts = new ArrayList<Pnt>();
    	for(Pnt pnt : this.toArray(new Pnt[0])){
    		if(!pnt.equals(currentPnt)){
    			pnts.add(pnt);
    		}
    	}
    	return new Line2(pnts.get(0),pnts.get(1));
    }

    /**
     * @return the triangle's circumcenter
     */
    public Pnt getCircumcenter () {
        if (circumcenter == null)
            circumcenter = Pnt.circumcenter(this.toArray(new Pnt[0]));
        return circumcenter;
    }

    /* The following two methods ensure that a Triangle is immutable */

    @Override
    public boolean add (Pnt vertex) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Iterator<Pnt> iterator () {
        return new Iterator<Pnt>() {
            private Iterator<Pnt> it = Triangle.super.iterator();
            public boolean hasNext() {return it.hasNext();}
            public Pnt next() {return it.next();}
            public void remove() {throw new UnsupportedOperationException();}
        };
    }

    /* The following two methods ensure that all triangles are different. */

    @Override
    public int hashCode () {
        return (int)(idNumber^(idNumber>>>32));
    }

    

    @Override
    public boolean equals (Object o) {
        return (this == o);
    }    
    
    public boolean pointInsideTriangle(Pnt pnt) {
    	double dX1=0.0,dX2=0.0,dX3=0.0,dY1=0.0,dY2=0.0,dY3=0.0,dY=0.0,dX=0.0;
    	dX = pnt.coord(0);
    	dY = pnt.coord(1);
    	int pointCounter = 0;
    	for(Pnt tPnt : this){
    		switch (pointCounter){
    		case 0:
    			dX1 = tPnt.coord(0);
    			dY1 = tPnt.coord(1);
    			break;
    		case 1:
    			dX2 = tPnt.coord(0);
    			dY2 = tPnt.coord(1);
    			break;
    		case 2:
    			dX3 = tPnt.coord(0);
    			dY3 = tPnt.coord(1);
    			break;
    		}
    		pointCounter++;
    	}
    	
    	int iCounter = 0;
        double xinters;
        double   slope;

        // ================================================================
        // Intersection of horizontal line and edge 1 (i.e., nodes 1 and 2)
        // ================================================================

        // Test. Is dY between the y values of nodes 1 and 2?...

        if (dY  > Math.min ( dY1, dY2 ) &&
            dY <= Math.max ( dY1, dY2 ) ) {

                if ( (dX <= Math.max( dX1, dX2 )) &&
                     (dY1 != dY2) ) {

                          // Compute slope (i.e., dx/dy) of edge ...

                          slope = (dX2 - dX1)/
                                  (dY2 - dY1);

                          // Compute x coordinate where edge intersects
                          // horizontal line....

                          xinters = ( dY - dY1)*slope + dX1;

                          // Increment counter for no of line crossings ...

                          if ( dX1 == dX2 || dX <= xinters ) 
                              iCounter = iCounter + 1;
                }
        }

        // ================================================================
        // Intersection of horizontal line and edge 2 (nodes 2 and 3)
        // ================================================================

        // Test. Is dY between the y values of nodes 2 and 3?...

        if (dY  > Math.min ( dY2, dY3 ) &&
            dY <= Math.max ( dY2, dY3 ) ) {

                if ( (dX <= Math.max( dX2, dX3 )) &&
                     (dY2 != dY3) ) {

                          // Compute slope (i.e., dx/dy) of edge ...

                          slope = (dX3 - dX2)/
                                  (dY3 - dY2);

                          // Compute x coordinate where edge intersects
                          // horizontal line....

                          xinters = ( dY - dY2)*slope + dX2;

                          // Increment counter for no of line crossings ...

                          if ( dX2 == dX3 || dX <= xinters) 
                              iCounter = iCounter + 1;
  
                }
        }

        // ================================================================
        // Intersection of horizontal line and edge 3 (nodes 3 and 1)
        // ================================================================

        // Test. Is dY between the y values of nodes 3 and 1?...

        if (dY  > Math.min ( dY3, dY1 ) &&
            dY <= Math.max ( dY3, dY1 ) ) {

                if ( (dX <= Math.max( dX3, dX1 )) &&
                     (dY3 != dY1) ) {

                          // Compute slope (i.e., dx/dy) of edge ...

                          slope = (dX1 - dX3)/
                                  (dY1 - dY3);

                          // Compute x coordinate where edge intersects
                          // horizontal line....

                          xinters = ( dY - dY3)*slope + dX3;

                          // Increment counter for no of line crossings ...

                          if ( dX3 == dX1 || dX <= xinters) 
                              iCounter = iCounter + 1;
                }
        }

        // Test to see if number of intersections is odd or even....

        if (iCounter % 2 == 0)
           return false;
        else
           return true;

     }

}